home *** CD-ROM | disk | FTP | other *** search
/ Aminet 23 / Aminet 23 (1998)(GTI - Schatztruhe)[!][Feb 1998].iso / Aminet / disk / misc / TransADF.lha / Source / pkzip.c < prev    next >
C/C++ Source or Header  |  1997-12-06  |  18KB  |  541 lines

  1. /*---------------------*/
  2. /* PKZip file routines */
  3. /*---------------------*/
  4.  
  5. #include <exec/types.h>
  6. #include <dos/dos.h>
  7. #include <clib/dos_protos.h>
  8.  
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <ctype.h>
  12.  
  13. #include "pkzip.h"
  14. #include "main.h"
  15. #include "util.h"
  16. #include "errors.h"
  17.  
  18. /* Private functions */
  19. LONG GotoFirstField (BPTR file, ULONG FieldID);
  20.  
  21.  
  22. /*----------------------------------*/
  23. /* Constants, structures and macros */
  24. /*----------------------------------*/
  25.  
  26. extern const char ZipComment[];  /* Defined in version.c */
  27.  
  28. /* PKZip Local Header structure */
  29. struct PKZHead {
  30.   ULONG  MagicNum;          /* Magic Number, = 0x504B0304 ('P','K',3,4).  */
  31.   UBYTE  MinVer;            /* Min. required UnZip version (20 for defl). */
  32.   UBYTE  MinOS;             /* Min. requires OS, MS-DOS = 0.              */
  33.   UWORD  Flags;             /* Flags.                                     */
  34.   UWORD  CMethod;           /* Compression method, deflate = 8.           */
  35.   ULONG  Date;              /* Last moddification date.                   */
  36.   ULONG  CRC;               /* Uncompressed Data CRC.                     */
  37.   ULONG  CSize;             /* Compressed data length.                    */
  38.   ULONG  USize;             /* Uncompressed data length.                  */ 
  39.   UWORD  FNameLen;          /* File name length.                          */
  40.   UWORD  EFieldLen;         /* Extra field length.                        */ 
  41. }; /* sizeof = 30 */
  42.  
  43.  
  44. /* PKZip Central Record structure */
  45. struct PKZCenRec {
  46.   ULONG  MagicNum;          /* Magic Number, = 0x504B0102 ('P','K',1,2).  */
  47.   UBYTE  ZipVer;            /* Zip version number x10 (ie 20 = v2.0).     */
  48.   UBYTE  HostOS;            /* Host OS, Amiga = 1.                        */
  49.   UBYTE  MinVer;            /* Min. required UnZip version (20 for defl). */
  50.   UBYTE  MinOS;             /* Min. requires OS, MS-DOS = 0.              */
  51.   UWORD  Flags;             /* Flags.                                     */
  52.   UWORD  CMethod;           /* Compression method, deflate = 8.           */
  53.   ULONG  Date;              /* Last moddification date.                   */
  54.   ULONG  CRC;               /* Uncompressed Data CRC.                     */
  55.   ULONG  CSize;             /* Compressed data length.                    */
  56.   ULONG  USize;             /* Uncompressed data length.                  */
  57.   UWORD  FNameLen;          /* File name length.                          */
  58.   UWORD  EFieldLen;         /* Extra field length.                        */
  59.   UWORD  FCommentLen;       /* File comment length.                       */
  60.   UWORD  DiskNum;           /* Disk number in multi-part zip.             */
  61.   UWORD  FileType;          /* File Type, set bit 0 for text.             */
  62.   UWORD  MSDAttrib;         /* MS-DOS File Attributes.                    */
  63.   UWORD  Attrib;            /* File Attributes, Amiga rw-d = 0x0D04 (LE). */
  64.   ULONG  LHeadOff;          /* Offset of local header.                    */
  65. }; /* sizeof = 46 */
  66.  
  67.  
  68. struct PKZEndCRec {
  69.   ULONG  MagicNum;          /* Magic Number, = 0x504B0506 ('P','K',5,6).  */ 
  70.   UWORD  DiskNum;           /* Number of this disk in multi-disk zip.     */
  71.   UWORD  CentRecDisk;       /* Disk number with start of Central Record.  */
  72.   UWORD  EntriesOnDisk;     /* Number of entries on this disk.            */
  73.   UWORD  CenRecEntries;     /* Total number of Central Record Entries.    */
  74.   ULONG  CenRecSize;        /* Total Size of Central Record.              */
  75.   ULONG  CenRecOffset;      /* Offset of start of Central Record.         */
  76.   UWORD  CommentLen;        /* Length of Zip Comment.                     */
  77. }; /* sizeof = 22 */
  78.  
  79.  
  80. #define LHD_MAGNUM   0x504B0304  /* Local Header Magic Number          */
  81. #define CRC_MAGNUM   0x504B0102  /* Central Record Magic Number        */
  82. #define ECR_MAGNUM   0x504B0506  /* End of Central Record Magic Number */
  83.  
  84.  
  85. /* Used to store the date/time that compression started */
  86. ULONG  startDate;
  87.  
  88. /* Used to store the location of the `current' PKZip header */
  89. LONG   pkzHeadOffset;
  90. STRPTR pkzOrigName;
  91.  
  92. /* Used to save the central record of archive when adding */
  93. ULONG Adding;  /* Set to 0x12345678 if adding, cleared afterwards */
  94. ULONG pkzOldCenRecSize;
  95. APTR  pkzOldCenRec;
  96.  
  97. /* End of central record - one per zip archive */
  98. struct PKZEndCRec pkzECRec;
  99.  
  100.  
  101. /*
  102. ** Output a PKZip Header to the specified file.
  103. ** Return TRUE if no errors. else FALSE.
  104. */
  105. BOOL writePKZHead (BPTR outFile, STRPTR origName)
  106. {
  107.   UBYTE Empty[30];
  108.   UWORD nlen;
  109.   
  110.   /* Simply write an empty sizeof (struct PKZHead) + strlen (origName). */
  111.   /* We'll fill in the details later.                                   */
  112.   
  113.   /* Note the date and time */
  114.   startDate = dosDate();
  115.   
  116.   /* Make sure we have a name */
  117.   if (!origName) origName = "disk.adf";
  118.   
  119.   /* Save the current position and name */
  120.   pkzHeadOffset = Seek (outFile, 0, OFFSET_CURRENT);
  121.   pkzOrigName = strdup (origName);
  122.   
  123.   /* Write the `header' and the name to the file */
  124.   if ( Write (outFile, Empty, 30) != 30) return FALSE;
  125.   
  126.   /* Get the filename length and write the filename */
  127.   nlen = strlen (origName);  
  128.   if ( Write (outFile, origName, nlen) != nlen) return FALSE;
  129.   
  130.   /* PKZip file is now ready to recieve the compressed data */
  131.   return TRUE;
  132. }
  133.  
  134.  
  135. /*
  136. ** Finish writing a PKZip file.
  137. ** Return TRUE if no errors. else FALSE.
  138. */
  139. BOOL finishPKZFile (BPTR outFile, ULONG CRC, ULONG CSize, ULONG USize)
  140. {
  141.   LONG  CRecPos;
  142.   ULONG Date;
  143.   UWORD FNameLen, ZCommentLen;
  144.   /* Local header - one per zipped file */
  145.   struct PKZHead pkzHead     = {LHD_MAGNUM,  /* PKZip Magic Number         */
  146.                                 20,          /* Min. UnZip version (2.0)   */
  147.                                 0,           /* Min. OS (MS-DOS)           */
  148.                                 0,           /* Flags                      */
  149.                                 0x0800,      /* Compression Method (LE)    */
  150.                                 0,           /* Date (to be filled in)     */
  151.                                 0,           /* CRC (to be filled in)      */
  152.                                 0,           /* CSize (to be filled in)    */
  153.                                 0,           /* USize (to be filled in)    */
  154.                                 0,           /* Filename length (tbfi)     */
  155.                                 0};          /* Extra field length         */
  156.   /* Central record entry - one per zipped file */
  157.   struct PKZCenRec pkzCenRec = {CRC_MAGNUM,  /* Central Rec Magic Number   */
  158.                                 20,          /* Zip version (2.0)          */
  159.                                 1,           /* Host OS (Amiga)            */
  160.                                 20,          /* Min. UnZip version (2.0)   */
  161.                                 0,           /* Min. Host OS (MS-DOS)      */
  162.                                 0,           /* Flags                      */
  163.                                 0x0800,      /* Compression Mode (LE)      */
  164.                                 0,           /* Date (to be filled in)     */
  165.                                 0,           /* CRC (to be filled in)      */
  166.                                 0,           /* CSize (to be filled in)    */
  167.                                 0,           /* USize (to be filled in)    */
  168.                                 0,           /* File Name Length (tbfi)    */
  169.                                 0,           /* Extra field length         */
  170.                                 0,           /* File comment length        */
  171.                                 0,           /* Disk Number                */
  172.                                 0,           /* File Type (binary)         */
  173.                                 0,           /* MS-DOS file attributes     */
  174.                                 0x0D04,      /* File Attributes (rw-d)     */
  175.                                 0};          /* Local header offset (tbfi) */
  176.   
  177.   
  178.   /* Seek to the local header and save the `current' position */
  179.   CRecPos = Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
  180.   
  181.   /* 'pre-process' CRC, CSize and USize */
  182.   CRC   = LEL (CRC);
  183.   CSize = LEL (CSize);
  184.   USize = LEL (USize);
  185.   Date  = LEL (startDate);
  186.   FNameLen = strlen (pkzOrigName);
  187.   ZCommentLen = strlen (ZipComment);
  188.   
  189.   /* Fill the structures */
  190.   pkzHead.Date       = Date;
  191.   pkzHead.CRC        = CRC;
  192.   pkzHead.CSize      = CSize;
  193.   pkzHead.USize      = USize;
  194.   pkzHead.FNameLen   = LES (FNameLen);
  195.   
  196.   pkzCenRec.Date     = Date;
  197.   pkzCenRec.CRC      = CRC;
  198.   pkzCenRec.CSize    = CSize;
  199.   pkzCenRec.USize    = USize;
  200.   pkzCenRec.FNameLen = LES (FNameLen);
  201.   pkzCenRec.LHeadOff = LEL (pkzHeadOffset);
  202.   
  203.   /* Fill the End of Central Record structure */
  204.   pkzECRec.MagicNum      = ECR_MAGNUM;
  205.   pkzECRec.DiskNum       = 0;
  206.   pkzECRec.CentRecDisk   = 0;
  207.   pkzECRec.CenRecOffset  = LEL (CRecPos);
  208.   pkzECRec.CommentLen    = LES (ZCommentLen);
  209.   if (Adding == 0x12345678)
  210.   {
  211.     pkzECRec.EntriesOnDisk = LES ( LES (pkzECRec.EntriesOnDisk) + 1);
  212.     pkzECRec.CenRecEntries = LES ( LES (pkzECRec.CenRecEntries) + 1);
  213.     pkzECRec.CenRecSize    = LEL (pkzOldCenRecSize + 46 + FNameLen);  
  214.   }
  215.   else
  216.   {
  217.     pkzECRec.EntriesOnDisk = LES (1);
  218.     pkzECRec.CenRecEntries = LES (1);
  219.     pkzECRec.CenRecSize    = LEL (46 + FNameLen);
  220.   }
  221.   
  222.   /* Write the Local Header and seek to the Central Record offset */
  223.   if ( Write (outFile, &pkzHead, 30) != 30) return FALSE;
  224.   /* Write (outFile, pkzOrigName, FNameLen); */ /* Already done */
  225.   Seek (outFile, CRecPos, OFFSET_BEGINNING);
  226.   
  227.   if (Adding == 0x12345678)
  228.   {
  229.     /* Write the old central record before adding the new one */
  230.     if ( Write (outFile, pkzOldCenRec, pkzOldCenRecSize) != pkzOldCenRecSize)
  231.       return FALSE;
  232.   }
  233.   
  234.   /* Write the Central Record and origName */
  235.   if ( Write (outFile, &pkzCenRec, 46) != 46) return FALSE;
  236.   if ( Write (outFile, pkzOrigName, FNameLen) != FNameLen) return FALSE;
  237.   
  238.   /* Write the End of Central Record and File Comment */
  239.   if ( Write (outFile, &pkzECRec, 22) != 22) return FALSE;
  240.   if ( Write (outFile, ZipComment, ZCommentLen) != ZCommentLen) return FALSE;
  241.   
  242.   /* Don't need the name anymore */
  243.   free (pkzOrigName);
  244.   
  245.   /* Don't need the old record anymore */
  246.   if (Adding == 0x123456780) free (pkzOldCenRec);
  247.   
  248.   /* No longer adding */
  249.   Adding = 0;
  250.   
  251.   /* Done! */
  252.   return TRUE;
  253. }
  254.  
  255.  
  256. /*
  257. ** Output a PKZip Header to the specified file, adding
  258. ** it to an existing archive.
  259. ** Return TRUE if no errors. else FALSE.
  260. */
  261. BOOL writePKZHeadAdd (BPTR outFile, STRPTR origName)
  262. {
  263.   LONG DOSError;
  264.  
  265.   /* We're adding */
  266.   Adding = 0x12345678;
  267.   FPuts (StdOut, "Updating archive.\n");
  268.  
  269.   /* Get the End of Central Record */
  270.   if ( GotoFirstField (outFile, ECR_MAGNUM) == -1)
  271.   {
  272.     DOSError = IoErr();
  273.     
  274.     if (DOSError)
  275.     {
  276.       FPrintf (StdErr, "%s: Error reading file - ", ProgName);
  277.       reportDOSError(DOSError);
  278.     }
  279.     else
  280.       FPrintf (StdErr, "%s: Can't add ADF, Not a Zip file.\n", ProgName);
  281.     
  282.     cleanExit (RETURN_ERROR, NULL);
  283.   }
  284.   if ( Read (outFile, &pkzECRec, 22) != 22) return FALSE;
  285.  
  286.   /* We'll be adding this header at the current position of the Cent Rec */
  287.   pkzHeadOffset = LEL (pkzECRec.CenRecOffset);
  288.   
  289.   /* Save the current central record for re-writing later */
  290.   pkzOldCenRecSize = LEL (pkzECRec.CenRecSize);
  291.   pkzOldCenRec = calloc (pkzOldCenRecSize, 1);
  292.   if (!pkzOldCenRec)
  293.   {
  294.     /* Memory error */
  295.     return FALSE;
  296.   }
  297.   Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);
  298.   if ( Read (outFile, pkzOldCenRec, pkzOldCenRecSize) != pkzOldCenRecSize)
  299.     return FALSE;
  300.   
  301.   /* Write our dummy header and get outa here */
  302.   Seek (outFile, pkzHeadOffset, OFFSET_BEGINNING);  
  303.   return writePKZHead (outFile, origName);
  304. }
  305.  
  306.  
  307. /*
  308. ** Finish writing a PKZip file, adding new a new record.
  309. ** Return TRUE if no errors. else FALSE.
  310. */
  311. BOOL finishPKZFileAdd (BPTR outFile, ULONG CRC, ULONG CSize, ULONG USize)
  312. {
  313.   return finishPKZFile (outFile, CRC, CSize, USize);
  314. }
  315.  
  316.  
  317. /*
  318. ** Skip the PKZip Header of a specified file.
  319. ** If origName is specified, search for that name within file.
  320. ** Return TRUE if no errors. else FALSE.
  321. */
  322. BOOL skipPKZHead (BPTR inFile, STRPTR origName)
  323. {
  324.   UBYTE FName[128];  /* This should be large enough */
  325.   struct PKZHead pkzHead;
  326.   struct PKZCenRec pkzCenRec;
  327.   ULONG origNameLen, pOrigNameLen;
  328.   LONG DOSError;
  329.   STRPTR pOrigName;
  330.   UWORD nlen, elen, clen;
  331.   int i;
  332.   
  333.   
  334.   if (origName)
  335.   {
  336.     /* Get the first central record entry */
  337.     if ( GotoFirstField (inFile, CRC_MAGNUM) == -1)
  338.     {
  339.       DOSError = IoErr();
  340.       
  341.       if (DOSError)
  342.       {
  343.         FPrintf (StdErr, "%s: Error reading file - ", ProgName);
  344.         reportDOSError(DOSError);
  345.       }
  346.       else
  347.         FPrintf (StdErr, "%s: Error - Not a Zip file.\n", ProgName);
  348.       
  349.       cleanExit (RETURN_ERROR, NULL);
  350.     }
  351.     
  352.     /* Parse the pattern */
  353.     origNameLen = strlen (origName);
  354.     for (i=0; i < origNameLen; i++)
  355.       origName[i] = toupper (origName[i]);
  356.     
  357.     pOrigNameLen = (2 * origNameLen) + 2;
  358.     pOrigName = calloc (pOrigNameLen, 1);
  359.     if (!pOrigName)
  360.     {
  361.       FPrintf (StdErr, "%s: Error - Not enough memory to match filename.\n",
  362.                        ProgName);
  363.       cleanExit (RETURN_FAIL, ERROR_NO_FREE_STORE);
  364.     }
  365.     
  366.     if (ParsePatternNoCase (origName, pOrigName, pOrigNameLen) == -1)
  367.     {
  368.       FPrintf (StdErr, "%s: Error - Can't match filename.\n", ProgName);
  369.       cleanExit (RETURN_FAIL, NULL);
  370.     }
  371.     
  372.     /* Now go through all the names and search for a match */
  373.     for (;;)
  374.     {
  375.       /* Read the entry */
  376.       if ( Read (inFile, &pkzCenRec, 46) != 46) return FALSE;
  377.       
  378.       /* Test the current magic number */
  379.       if (pkzCenRec.MagicNum != CRC_MAGNUM)
  380.       {
  381.         /* We went through the whole list without a match */
  382.         FPrintf (StdErr, "%s: No match for filename.\n", ProgName);
  383.         cleanExit (RETURN_WARN, NULL);
  384.       }
  385.       
  386.       /* Get extra info lengths */
  387.       nlen = LES (pkzCenRec.FNameLen);
  388.       elen = LES (pkzCenRec.EFieldLen);
  389.       clen = LES (pkzCenRec.FCommentLen);
  390.       
  391.       /* Read in the name */
  392.       if ( Read (inFile, FName, nlen) != nlen) return FALSE;
  393.       FName[nlen] = 0;
  394.       
  395.       /* Attempt to match the pattern */
  396.       if (MatchPatternNoCase (pOrigName, FName))
  397.       {
  398.         /* Output information */
  399.         FPrintf (StdOut, "Extracting %s.\n", FName);
  400.       
  401.         /* Got a name, seek to the start of this item */
  402.         Seek (inFile, LEL (pkzCenRec.LHeadOff), OFFSET_BEGINNING);
  403.         
  404.         /* Break the loop */
  405.         break;
  406.       }
  407.       
  408.       /* No match, move along */
  409.       Seek (inFile, elen + clen, OFFSET_CURRENT);
  410.     }
  411.     
  412.     free (pOrigName);
  413.   }
  414.   
  415.   /* Save the current position */
  416.   pkzHeadOffset = Seek (inFile, 0, OFFSET_CURRENT);
  417.   
  418.   /* Consume the header and extra info */
  419.   if ( Read (inFile, &pkzHead, 30) != 30) return FALSE;
  420.   if (pkzHead.MagicNum != LHD_MAGNUM)
  421.   {
  422.     FPrintf (StdErr, 
  423.              "%s: Error - Didn't read a Local header - not a Zip file.\n",
  424.              ProgName);
  425.     cleanExit (RETURN_ERROR, NULL);    
  426.   }
  427.   nlen = LES (pkzHead.FNameLen);
  428.   elen = LES (pkzHead.EFieldLen);
  429.   Seek (inFile, (nlen + elen), OFFSET_CURRENT);
  430.   
  431.   /* Check the compression */
  432.   if ( LES (pkzHead.CMethod) != 8)
  433.   {
  434.     FPrintf (StdErr,
  435.              "%s: Error - Only Deflate compression method supported.\n",
  436.              ProgName);
  437.     cleanExit (RETURN_ERROR, NULL);
  438.   }
  439.   
  440.   /* inFile now points to the compressed data */
  441.   return TRUE;
  442. }
  443.  
  444.  
  445. /*
  446. ** Read the header of a PKZip file and return the CRC
  447. ** and USize in supplied arrays.
  448. ** Return TRUE if no errors, else FALSE.
  449. */
  450. BOOL readPKZTail (BPTR inFile, ULONG *CRC, ULONG *USize)
  451. {
  452.   struct PKZHead pkzHead;
  453.   LONG oldp;
  454.   
  455.   /* Get the `current' header */
  456.   oldp = Seek (inFile, pkzHeadOffset, OFFSET_BEGINNING);
  457.   if ( Read (inFile, &pkzHead, 30) != 30) return FALSE;
  458.   Seek (inFile, oldp, OFFSET_BEGINNING);
  459.   
  460.   /* Fill the return values */
  461.   *CRC   = LEL (pkzHead.CRC);
  462.   *USize = LEL (pkzHead.USize);
  463.   
  464.   return TRUE;
  465. }
  466.  
  467.  
  468. /*
  469. ** Jump to the start of the first field with FieldID.
  470. ** Return the offset as well as setting the file pointer.
  471. ** Return -1 for error.
  472. */
  473. LONG GotoFirstField (BPTR file, ULONG FieldID)
  474. {
  475.   UBYTE Buffer[46];
  476.   /* Convenient `gateways' */
  477.   struct PKZHead *pkzHead = (struct PKZHead *) Buffer;
  478.   struct PKZCenRec *pkzCenRec = (struct PKZCenRec *) Buffer;
  479.   ULONG item, nextSeek;
  480.   int cont = TRUE;
  481.   
  482.   
  483.   do 
  484.   {
  485.     /* Read in an item */
  486.     if ( Read (file, &item, 4) != 4) return -1;
  487.     
  488.     /* Identify item and get the rest of the field if required */
  489.     switch (item) {
  490.     case LHD_MAGNUM:
  491.       /* Local header */
  492.       
  493.       /* Check if want to stop here */
  494.       if (FieldID == LHD_MAGNUM)
  495.         cont = FALSE;
  496.       else
  497.       {
  498.         /* Move along */
  499.         if ( Read (file, Buffer+4, 26) != 26) return -1;  /* 26 = 30 - 4 */
  500.         nextSeek = LES (pkzHead->FNameLen) + LES (pkzHead->EFieldLen) + 
  501.                    LEL (pkzHead->CSize);
  502.         Seek (file, nextSeek, OFFSET_CURRENT);
  503.       }
  504.       break;
  505.     
  506.     case CRC_MAGNUM:
  507.       /* Central record entry */
  508.       
  509.       /* Check if want to stop here */
  510.       if (FieldID == CRC_MAGNUM)
  511.         cont = FALSE;
  512.       else
  513.       {
  514.         /* Move along */
  515.         if ( Read (file, Buffer+4, 42) != 42) return -1;  /* 42 = 46 - 4 */
  516.         nextSeek = LES (pkzCenRec->FNameLen) + LES (pkzCenRec->EFieldLen) + 
  517.                    LES (pkzCenRec->FCommentLen);
  518.         Seek (file, nextSeek, OFFSET_CURRENT);
  519.       }
  520.       break;
  521.     
  522.     case ECR_MAGNUM:
  523.       /* End of central record */
  524.       
  525.       /* Final destination - stop here! */
  526.       cont = FALSE;
  527.       break;
  528.     
  529.     default:
  530.       /* Can't id the field - error */
  531.       return -1;
  532.     }    
  533.   } while (cont);
  534.   
  535.   /* Seek back to the start of the current item */
  536.   Seek (file, -4, OFFSET_CURRENT);
  537.   
  538.   /* return the current offset */
  539.   return Seek (file, 0, OFFSET_CURRENT);
  540. }
  541.